/* Includes ------------------------------------------------------------------*/

/* Private includes ----------------------------------------------------------*/
#include "main.h"
#include "tx_api.h"
#include "shell.h"
#include "byte_fifo.h"

/* Private typedef -----------------------------------------------------------*/

/* Private define ------------------------------------------------------------*/
#define TASK_STACK_SIZE 10240
#define RX_TX_BUF_SIZE 256

/* Private macro -------------------------------------------------------------*/

/* Global variables ---------------------------------------------------------*/

/* External function ---------------------------------------------------------*/
extern UART_HandleTypeDef huart2;

/* External variables ---------------------------------------------------------*/

/* Private variables ---------------------------------------------------------*/
static uint8_t tx_buf[RX_TX_BUF_SIZE] __attribute__((section(".RAM1_Section")));
static char shell_buffer[1024];
static ByteFifo rx_fifo __attribute__((section(".RAM1_Section")));
    
static uint8_t rx_data;
static Shell shell;
static uint8_t task_stack[TASK_STACK_SIZE] __attribute__((section(".RAM1_Section")));
static TX_THREAD tx_task;
static TX_SEMAPHORE tx_semaphore_receive;

/* Private function prototypes -----------------------------------------------*/
// static signed short shell_read(char *data, unsigned short len);
static signed short shell_write(char *data, unsigned short len);
/* Public Function definitions -----------------------------------------------*/
HAL_StatusTypeDef uart_write(uint8_t *data, int len, int using_dma);

/* Private Function definitions -----------------------------------------------*/
void HAL_UART_RxCpltCallback(UART_HandleTypeDef *huart)
{
    UNUSED(huart);

    byte_fifo_write(&rx_fifo, rx_data);
    tx_semaphore_put(&tx_semaphore_receive); // Signal the semaphore to indicate data is ready
    HAL_UART_Receive_IT(huart, &rx_data, 1); // Receive data in interrupt mode
}

void HAL_UART_TxCpltCallback(UART_HandleTypeDef *huart)
{
    UNUSED(huart);
}

signed short shell_write(char *data, unsigned short len)
{
    HAL_StatusTypeDef status = uart_write((uint8_t *)data, len, 0);
    if (status == HAL_OK)
    {
        return len;
    }
    else
        return 0;
}

static void task_entry(ULONG thread_input)
{
    uint8_t byte_data;

    // shell.read = shell_read;
    shell.write = shell_write;
    shellInit(&shell, shell_buffer, sizeof(shell_buffer));

    HAL_UART_Receive_IT(&huart2, &rx_data, 1); // Receive data in interrupt mode
    while (1)
    {
        tx_semaphore_get(&tx_semaphore_receive, TX_WAIT_FOREVER); // Wait for semaphore

        // HAL_NVIC_DisableIRQ(USART2_IRQn);            // Disable USART2 interrupt
        // HAL_NVIC_EnableIRQ(USART2_IRQn); // Re-enable USART2 interrupt

        while (!byte_fifo_is_empty(&rx_fifo))
        {
            byte_data = byte_fifo_read(&rx_fifo);
            shellHandler(&shell, byte_data); // Process the received character
        }
    }
}

static void software_reset()
{
    NVIC_SystemReset();
}

static int device()
{
    return 0x4d4c4355; // ASCII 'MLCU'
}

static int version()
{
    return 0x00000001; // u16+u16: big version + little version
}

/* Public Function definitions -----------------------------------------------*/
void letter_shell_init()
{
    byte_fifo_init(&rx_fifo);

    tx_semaphore_create(&tx_semaphore_receive, "tx_semaphore_receive", 0);
    tx_thread_create(&tx_task, "Shell Thread", task_entry, 0,
                     task_stack, sizeof(task_stack),
                     2, 2,
                     TX_NO_TIME_SLICE, TX_AUTO_START);
}

/**
 * @brief 串口发送数据
 * @param data 发送数据指针
 * @param len 发送数据长度
 * @param using_dma 是否使用dma发送
 */
HAL_StatusTypeDef uart_write(uint8_t *data, int len, int using_dma)
{
    HAL_StatusTypeDef status;

    if (len <= 0 || data == NULL)
    {
        return HAL_ERROR;
    }

    // 必须死等! 如果使用操作系统的函数等候, 导致任务切换会出错
    while (huart2.gState != HAL_UART_STATE_READY)
    {
        // tx_thread_sleep(1);
    }

    if (!using_dma)
    {
        /*
        用dma发送会出错, 原因还未找到
        */
        // memset(tx_buf, 0, sizeof(tx_buf));
        memcpy(tx_buf, data, len); // Copy data to tx_buf
        // tx_buf[len] = 0;
        data = tx_buf;

        // status = HAL_UART_Transmit_IT(&huart2, (uint8_t *)data, len);
        // return status;
    }

    status = HAL_UART_Transmit_DMA(&huart2, data, len); // Transmit data using DMA
    if (status != HAL_OK)
    {
        Error_Handler();
    }

    return status;
}
/* Testbench ------------------------------------------------------------*/
// static uint8_t task_stack_test[TASK_STACK_SIZE];
// static TX_THREAD tx_task_test;

// static void task_test(ULONG arg)
// {
//     UNUSED(arg);
//     extern unsigned short shellWriteString(Shell * shell, const char *string);
//     extern signed char shellToDec(int value, char *buffer);
//     extern signed char shellToHex(unsigned int value, char *buffer);

//     int value = 0x4d4c4355; // ASCII 'MLCU'

//     while (1)
//     {
//         tx_thread_sleep(500);

//         char buffer[12] = "00000000000";
//         shellWriteString(&shell, "Return: ");
//         shellWriteString(&shell, &buffer[11 - shellToDec(value, buffer)]);
//         shellWriteString(&shell, ", 0x");
//         for (short i = 0; i < 11; i++)
//         {
//             buffer[i] = '0';
//         }
//         shellToHex(value, buffer);
//         shellWriteString(&shell, buffer);
//         shellWriteString(&shell, "\r\n");
//     }
// }

// static int test()
// {
//     tx_thread_create(&tx_task_test, "Shell Thread test", task_test, 0,
//                      task_stack_test, sizeof(task_stack_test),
//                      10, 10,
//                      TX_NO_TIME_SLICE, TX_AUTO_START);
//     return 0;
// }

/****************************************************************/
SHELL_EXPORT_CMD(SHELL_CMD_PERMISSION(0) | SHELL_CMD_TYPE(SHELL_TYPE_CMD_FUNC), reset, software_reset, );
SHELL_EXPORT_CMD(SHELL_CMD_PERMISSION(0) | SHELL_CMD_TYPE(SHELL_TYPE_CMD_FUNC), device, device, device name);
SHELL_EXPORT_CMD(SHELL_CMD_PERMISSION(0) | SHELL_CMD_TYPE(SHELL_TYPE_CMD_FUNC), version, version, version);
